package transport

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"

	"gitee.com/jason_elva8325/micro-quickstart/common"
	"gitee.com/jason_elva8325/micro-quickstart/endpoint"
	"gitee.com/jason_elva8325/micro-quickstart/service"
	kitendpoint "github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	httptransport "github.com/go-kit/kit/transport/http"
	"github.com/gorilla/mux"
)

// NewHTTPHandler 新建HTTP服务器方法
func NewHTTPHandler(endpoints endpoint.Set, logger log.Logger) http.Handler {
	options := []httptransport.ServerOption{
		httptransport.ServerErrorEncoder(errorEncoder),
		httptransport.ServerErrorLogger(logger),
	}
	m := mux.NewRouter().PathPrefix("/say").Subrouter()
	m.Methods("POST").Path("/hello").Handler(httptransport.NewServer(
		endpoints.SayHelloEndpoint,
		decodeHTTPSayHelloRequest,
		encodeHTTPGenericResponse,
		append(options, httptransport.ServerBefore(HTTPToContext()))...,
	))
	m.Methods("GET", "POST").Path("/goodby/{lang}").Handler(httptransport.NewServer(
		endpoints.SayGoodbyEndpoint,
		decodeHTTPSayGoodbyRequest,
		encodeHTTPGenericResponse,
		append(options, httptransport.ServerBefore(HTTPToContext()))...,
	))
	return m
}

func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
	w.WriteHeader(err2code(err))
	json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()})
}

func err2code(err error) int {
	switch err {
	case service.ErrInvalidLang, ErrBadRouting:
		return http.StatusBadRequest
	default:
		return http.StatusInternalServerError
	}
}

type errorWrapper struct {
	Error string `json:"error"`
}

// 自定义错误类型
var (
	ErrBadRouting = errors.New("inconsistent mapping between route and handler")
)

func decodeHTTPSayHelloRequest(_ context.Context, r *http.Request) (interface{}, error) {
	var req endpoint.SayHelloRequest
	err := json.NewDecoder(r.Body).Decode(&req)
	return req, err
}

func decodeHTTPSayGoodbyRequest(_ context.Context, r *http.Request) (interface{}, error) {
	vars := mux.Vars(r)
	lang, ok := vars["lang"]
	if !ok {
		return "", ErrBadRouting
	}
	target, ok := r.URL.Query()["target"]
	if !ok && len(target) != 1 { // 仅接受第一个target参数
		return "", ErrBadRouting
	}
	req := endpoint.SayGoodbyRequest{
		Lang:   lang,
		Target: target[0],
	}
	return req, nil
}

func encodeHTTPGenericResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
	if f, ok := response.(kitendpoint.Failer); ok && f.Failed() != nil {
		errorEncoder(ctx, f.Failed(), w)
		return nil
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(response)
}

// HTTPToContext 将HTTP中的header信息转换为上下文信息
func HTTPToContext() httptransport.RequestFunc {
	return func(ctx context.Context, r *http.Request) context.Context {
		sessionIDHeader := r.Header.Get("Sessionid")
		return context.WithValue(ctx, common.ContextKey, sessionIDHeader)
	}
}
